﻿#region using statements

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using NGen.Managers.Logging;

using Nuclex.UserInterface;

#endregion

namespace NGen.Managers.GUI
{
	/// <summary>
	/// 	Class object used to manage a GUI. Extends the Nuclex GuiManager class
	/// </summary>
	public sealed class ManagerGUI : GameManager, IManagerGUI
	{
		#region local fields

		/// <summary>
		/// 	instance of the Nuclex library GuiManager
		/// </summary>
		private readonly GuiManager m_guiManager;

		/// <summary>
		/// 	Maintains a collection of all the loaded GUIElements
		/// </summary>
		private readonly LinkedList<GUIElement> m_lElements = new LinkedList<GUIElement>();

		#endregion

		#region constructor

		public ManagerGUI() {
			m_guiManager = new GuiManager(GameEngine.GameServices);
			CurrentLoggingLevel = LoggingLevel.MINIMAL;
			Logger = GameEngine.GetService<IManagerLogs>().Log<ManagerGUI>();
			SkipUpdate = SkipDraw = false;
			AllowButtonsToKeepFocus = false;
			UpdateOrder = 1;
			DrawOrder = 400;
		}

		#endregion

		#region IGameManager implementation

		/// <summary>
		/// 	initialize the manager
		/// </summary>
		internal override void Initialize() {
			m_guiManager.Screen = new Screen(GameEngine.GraphicsDeviceManager.GraphicsDevice.Viewport.Width, GameEngine.GraphicsDeviceManager.GraphicsDevice.Viewport.Height);
			ScreenResolutionChanged();
			m_guiManager.Initialize();
			m_guiManager.Screen.Desktop.Bounds = new UniRectangle(new UniVector(0, 0), new UniVector(m_guiManager.Screen.Width, m_guiManager.Screen.Height));
		}

		/// <summary>
		/// 	update the manager
		/// </summary>
		/// <param name = "p_gameTime">snapshot of timing values</param>
		internal override void Update(GameTime p_gameTime) {
			foreach(var element in m_lElements) {
				element.Update(p_gameTime);
			}

			if(!AllowButtonsToKeepFocus) {
				m_guiManager.Screen.FocusedControl = null;
			}

			m_guiManager.Update(p_gameTime);
		}

		/// <summary>
		/// 	draw the manager
		/// </summary>
		/// <param name = "p_gameTime">snapshot of timing values</param>
		internal override void Draw(GameTime p_gameTime) {
			m_guiManager.Draw(p_gameTime);

			GameEngine.SpriteBatch.Begin();
			foreach(var element in m_lElements) {
				element._Draw(p_gameTime);
			}
			GameEngine.SpriteBatch.End();
		}

		/// <summary>
		/// 	cleanup the manager
		/// </summary>
		internal override void Cleanup() {
			while(m_lElements.Count > 0) {
				m_lElements.Last.Value._Cleanup();
				try { //catch an exception in case an element removes itself in its own cleanup method
					m_lElements.RemoveLast();
				} catch(Exception) {}
			}

			m_guiManager.Screen.Desktop.Children.Clear();
		}

		#endregion

		#region IManagerGUI implementation

		/// <summary>
		/// 	Get or set the ability for buttons to maintain focus when clicked.
		/// 	If a button has focus, pressing space will activate the button.
		/// 	This is set to false by default.
		/// </summary>
		public bool AllowButtonsToKeepFocus { get; set; }

		/// <summary>
		/// 	Reload the GUI, useful if any changes were made within elements
		/// </summary>
		public void RefreshGUI() {
			m_guiManager.Screen.Desktop.Children.Clear();

			for(var element = m_lElements.Last; element != null; element = element.Previous) {
				foreach(var control in element.Value._Children) {
					m_guiManager.Screen.Desktop.Children.Add(control);

					control.BringToFront();
				}
			}
		}

		/// <summary>
		/// 	Add an element to the GUI, if it is not already added
		/// </summary>
		/// <param name = "p_element">element to add</param>
		public void AddElement(GUIElement p_element) {
			if(!m_lElements.Contains(p_element)) {
				p_element._Initialize();
				m_lElements.AddFirst(p_element);
				Logger.Write(Logger.BuildString(GetType().Name).Append(": element added -> ").Append(p_element.GetType().Name).ToString(), CurrentLoggingLevel);
			}
			RefreshGUI();
		}

		/// <summary>
		/// 	Remove an element from the GUI
		/// </summary>
		/// <param name = "p_element">element to remove</param>
		public void RemoveElement(GUIElement p_element) {
			if(m_lElements.Remove(p_element)) {
				p_element._Cleanup();
				Logger.Write(Logger.BuildString(GetType().Name).Append(": element removed -> ").Append(p_element.GetType().Name).ToString(), CurrentLoggingLevel);
			}
			RefreshGUI();
		}

		/// <summary>
		/// 	Clears the GUI, cleaning up all added objects
		/// </summary>
		public void ClearGUI() {
			foreach(var element in m_lElements) {
				element._Cleanup();
			}
			m_lElements.Clear();
			m_guiManager.Screen.Desktop.Children.Clear();

			Logger.Write(Logger.BuildString(GetType().Name).Append(": gui cleared").ToString(), CurrentLoggingLevel);
		}

		/// <summary>
		/// 	Returns an array containing all of the GUIElements which are
		/// 	currently loaded
		/// </summary>
		/// <returns>an array of all loaded GUIElements</returns>
		public GUIElement[] GetElementsList() {
			var elementArray = new GUIElement[m_lElements.Count];
			m_lElements.CopyTo(elementArray, 0);
			return elementArray;
		}

		/// <summary>
		/// 	Returns a string array containing all of the currently visible
		/// 	layouts
		/// </summary>
		/// <returns>the list of element names</returns>
		public string[] GetElementsNames() {
			var stringArray = new string[m_lElements.Count];
			var itr = m_lElements.GetEnumerator();

			for(var i = 0; i < stringArray.Length; ++i) {
				itr.MoveNext();
				if(itr.Current != null) {
					stringArray[i] = itr.Current.ToString();
				}
			}
			return stringArray;
		}

		/// <summary>
		/// 	Notify the GUI of a screen resolution change
		/// </summary>
		public void ScreenResolutionChanged() {
			m_guiManager.Screen.Height = GameEngine.GraphicsDeviceManager.PreferredBackBufferHeight;
			m_guiManager.Screen.Width = GameEngine.GraphicsDeviceManager.PreferredBackBufferWidth;
			m_guiManager.Screen.Desktop.Bounds = new UniRectangle(new UniVector(0, 0), new UniVector(m_guiManager.Screen.Width, m_guiManager.Screen.Height));
			m_guiManager.Initialize();
			Logger.Write(Logger.BuildString("Screen resolution changed").ToString(), CurrentLoggingLevel);
		}

		/// <summary>
		/// 	Check if the mouse cursor is over a GUI element
		/// </summary>
		/// <returns>true if the mouse is over a GUI element</returns>
		public bool MouseOverGUI() {
			return m_guiManager.Screen.IsMouseOverGui;
		}

		#endregion
	}
}